package ref

import (
	"errors"
	"reflect"
	"strings"
)

var errInvalidEntity = errors.New("invalid entity")

// InsertStmt 作业里面我们这个只是生成 SQL，所以在处理 sql.NullString 之类的接口
// 只需要判断有没有实现 driver.Valuer 就可以了
func InsertStmt(entity interface{}) (string, []interface{}, error) {

	if entity == nil {
		return "", nil, errInvalidEntity
	}
	val := reflect.ValueOf(entity)
	typ := val.Type()
	// 检测 entity 是否符合我们的要求
	// 我们只支持有限的几种输入
	if typ.Kind() == reflect.Pointer {
		// 这里调用 Elem() 是因为指针，需要通过 Elem() 来取到指针指向的对象
		val = val.Elem()
		typ = val.Type()
	}

	if typ.Kind() != reflect.Struct || typ.Size() == 0 {
		return "", nil, errInvalidEntity
	}

	typName := typ.Name()
	// 使用 strings.builder 来拼接 字符串
	// bd := strings.builder{}
	sb := strings.Builder{}
	// 13 -> INSERT INTO` | 2 -> `(
	// 9 -> ) VALUES( | 2 -> );
	sb.Grow(13 + len(typName) + 2 + 9 + 2)
	// 构造 INSERT INTO XXX，XXX 是你的表名，这里我们直接用结构体名字
	_, _ = sb.WriteString("INSERT INTO `")
	sb.WriteString(typName)
	sb.WriteString("`(")

	// 遍历所有的字段，构造出来的是 INSERT INTO XXX(col1, col2, col3)
	// 在这个遍历的过程中，你就可以把参数构造出来
	// 如果你打算支持组合，那么这里你要深入解析每一个组合的结构体
	// 并且层层深入进去
	// 这里因为可能设计到子结构体，所以用函数来递归是最方便的
	fields, values := fieldNameAndValues(val)

	// 拼接 VALUES，达成 INSERT INTO XXX(col1, col2, col3) VALUES
	for i, name := range fields {
		sb.Grow(len(name) + 3)
		if i > 0 {
			sb.WriteRune(',')
		}
		sb.WriteRune('`')
		sb.WriteString(name)
		sb.WriteRune('`')
	}

	// 再一次遍历所有的字段，要拼接成 INSERT INTO XXX(col1, col2, col3) VALUES(?,?,?)
	// 注意，在第一次遍历的时候我们就已经拿到了参数的值，所以这里就是简单拼接 ?,?,?
	sb.WriteString(") VALUES(")
	// 因为 ? 和 , 是成对出现，但是 , 总比 ? 少一个
	sb.Grow(len(fields)*2 - 1)
	args := make([]interface{}, 0, len(values))
	for i, fd := range fields {
		if i > 0 {
			sb.WriteRune(',')
		}
		sb.WriteRune('?')
		args = append(args, values[fd])
	}
	sb.WriteString(");")
	return sb.String(), args, nil

	//// return bd.String(), args, nil
	//panic("implement me")
}

// fieldNameAndValues
// 传入一个 reflect.Value
// 输出： []string 子属性名； map[string]interface{} 子属性值
// 如果有重复的子属性，以第一个为准
func fieldNameAndValues(val reflect.Value) ([]string, map[string]interface{}) {
	typ := val.Type()
	fieldNum := typ.NumField()
	fields := make([]string, 0, fieldNum)
	values := make(map[string]interface{}, fieldNum)
	for i := 0; i < fieldNum; i++ {
		field := typ.Field(i)
		fieldVal := val.Field(i)
		// 如果有子结构体，并且匿名（就是声明的时候没有声明属性名）
		// type Seller struct { UserInfo User } -> 非匿名
		// type Seller struct { User } -> 匿名
		if field.Type.Kind() == reflect.Struct && field.Anonymous {
			subFields, subValues := fieldNameAndValues(fieldVal)
			for _, k := range subFields {
				if _, ok := values[k]; ok {
					// 子结构里面的重复字段，直接忽视，只取第一个
					continue
				}
				fields = append(fields, k)
				values[k] = subValues[k]
			}
			continue
		}
		// 没有子结构需要处理，那就正常循环
		fields = append(fields, field.Name)
		values[field.Name] = fieldVal.Interface()
	}
	return fields, values
}
